perm filename INITEX.CH[TEX,DEK]7 blob
sn#707853 filedate 1983-04-17 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00052 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00008 00002 \def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
C00009 00003 The |banner| string defined here should be changed whenever \TeX\
C00010 00004 @ Some of the code below is intended to be used only when diagnosing the
C00012 00005 @ This program has two important variations: (1) There is a long and slow
C00014 00006 @<Compiler directives@>=
C00015 00007 @ The following parameters can be changed at compile time to extend or
C00019 00008 One can't simply make helter-skelter changes to the following constants,
C00021 00009 @ The ascii code is ``standard'' only to a certain extent, since many
C00023 00010 @p function a_open_in(var f:alpha_file):boolean
C00026 00011 @ Input from text files is read one line at a time, using a routine called
C00029 00012 @p function input_ln(var f:alpha_file):boolean {inputs the next line
C00031 00013 @ The user's terminal acts essentially like other files of text, except
C00032 00014 @ Here is how to open the terminal files
C00033 00015 @d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
C00034 00016 @ The following program does the required initialization
C00039 00017 @<Character |k| cannot be printed@>=
C00040 00018 @p procedure term_input {gets a line from the terminal}
C00041 00019 @ It is desirable to provide an `\.E' option here that gives the user
C00044 00020 It is usually most efficient to have |min_quarterword=min_halfword=0|,
C00045 00021 The inner loop of \TeX\ will run faster with respect to compilers
C00047 00022 cat_code(carriage_return)←car_ret cat_code(" ")←spacer
C00049 00023 @ The following procedure, which is called just before \TeX\ initializes its
C00050 00024 The global variable |line| contains the line number in the topmost
C00052 00025 @<Print location of current line@>=
C00053 00026 @ The |begin_file_reading| procedure starts a new level of input for lines
C00055 00027 @ Conversely, the variables must be downdated when such a level of input
C00056 00028 @p procedure check_outer_validity
C00058 00029 @ If the user has set the |pausing| parameter to some nonzero value,
C00060 00030 @ The first line of a file must be treated specially, since |input_ln|
C00062 00031 @ When we skip conditional text, we keep track of the line number
C00063 00032 @p procedure pass_text
C00064 00033 @ The file names we shall deal with have the following structure:
C00066 00034 @ Input files that can't be found in the user's area may appear in a standard
C00067 00035 @ And here's the second.
C00068 00036 @ The third.
C00069 00037 @ Conversely, here is a routine that takes three strings and prints a file
C00070 00038 @ Another system-dependent routine is needed to convert three \TeX\ strings
C00073 00039 @ A messier routine is also needed, since format file names must be scanned
C00074 00040 @ @<Set init...@>=
C00075 00041 @ Here is the messy routine that was just mentioned. It sets |name_of_file|
C00079 00042 @ Operating systems often make it possible to determine the exact name (and
C00081 00043 @p procedure prompt_file_name(@!s,@!e:str_number)
C00083 00044 @ Here we have to remember that the |input_ln| routine
C00086 00045 @!dvi_index=0..dvi_buf_size {an index into the output buffer}
C00087 00046 @ Some systems may find it more efficient to make |dvi_buf| a |packed|
C00088 00047 @ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
C00090 00048 @<Finish the \.{DVI} file@>=
C00093 00049 @ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
C00095 00050 @ @<Finish issuing a diagnostic message for an overfull or underfull vbox@>=
C00096 00051 @ Here we do whatever is needed to complete \TeX's job gracefully
C00098 00052 @* \[54] System-dependent changes.
C00103 ENDMK
C⊗;
\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
\setcount0 3
\let\maybe=\iffalse
@z
The |banner| string defined here should be changed whenever \TeX\
undergoes any modifications, so that it will be clear which version of
\TeX\ might be the guilty party when a problem arises.
@↑extensions to \TeX@>
@d banner=='This is TeX, WAITS Version 0.97' {printed when \TeX\ starts}
@↑system dependencies@>
@z
@ Some of the code below is intended to be used only when diagnosing the
strange behavior that sometimes occurs when \TeX\ is being installed or
when system wizards are fooling around with \TeX\ without quite knowing
what they are doing. Such code will not normally be compiled; it is
delimited by the codewords `$|debug|\ldots|gubed|$', with apologies
to people who wish to preserve the purity of English. Similarly, there
is some conditional code delimited by `$|stat|\ldots|tats|$'
that is intended only for use when statistics
are to be kept about \TeX's memory usage.
@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
@f debug==begin
@f gubed==end
@#
@d stat== {change this to `$\\{stat}\equiv\.{@@\{}$' when not
gathering usage statistics}
@d tats== {change this to `$\\{tats}\equiv\.{@@\}}$' when not
gathering usage statistics}
@f stat==begin
@f tats==end
@↑system dependencies@>
@z
@ This program has two important variations: (1) There is a long and slow
version called \.{INITEX}, which does the extra calculations need to
@.INITEX@>
initialize \TeX's internal tables; and (2)@@there is a shorter and faster
production version, which cuts the initialization to a bare minimum.
Parts of the program that are needed in (1) but not in (2) are delimited by
the codewords `$|init|\ldots|tini|$'.
(Here we are not really changing \.{TEX.WEB}; this module is included in the
change file so that \.{INITEX.CH} and \.{TEX.CH} have the same basic structure.)
@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
@f init==begin
@f tini==end
@<Initialize whatever...@>=
@<Set initial values of key variables@>@/
@!init @<Initialize table entries (done by \.{INITEX} only)@>@;@+tini
@↑system dependencies@>
@z
@<Compiler directives@>=
@{@&$C-,A+,D-,W+,H:0@}
{no range check, catch arithmetic overflow, no debug overhead, no heap}
@!debug @{@&$C+,D:5,W+@}@+ gubed {but turn everything on when debugging}
{the `|W+|' switch catches more syntax errors}
{the `\ignorespaces|D:5|' avoids initial stop for the debugger}
@↑system dependencies@>
@z
@ The following parameters can be changed at compile time to extend or
reduce \TeX's capacity. They may have different values in \.{INITEX} and
in production versions of \TeX.
@.INITEX@>
@↑system dependencies@>
@<Constants...@>=
@!mem_max=33000; {greatest index in \TeX's internal |mem| array,
must be strictly less than |max_halfword|;
also, for \ph, we must keep total variable storage < 128K words}
@!buf_size=500; {maximum number of characters simultaneously present in
current lines of open files and in control sequences between
\.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
@!error_line=80; {width of context lines on terminal error messages}
@!half_error_line=50; {width of first lines of contexts in terminal
error messages, should be between 30 and |error_line-15|}
@!max_print_line=80; {width of longest text lines output, should be at least 60}
@!stack_size=80; {maximum number of simultaneous input sources}
@!max_in_open=6; {maximum number of input files and error insertions that
can be going on simultaneously}
@!font_max=100;
{maximum internal font number, must not exceed |max_quarterword|}
@!font_mem_size=23000; {number of words of |font_info| for all fonts}
@!param_size=30; {maximum number of simultaneous macro parameters}
@!nest_size=40; {maximum number of semantic levels simultaneously active}
@!max_strings=3000; {maximum number of strings}
@!string_vacancies=8000; {the minimum number of characters that should be
available for the user's control sequences and font names,
after \TeX's own error messages are stored}
@!pool_size=32000; {maximum number of characters in strings, including all
error messages and help texts, and the names of all fonts and
control sequences; must exceed |string_vacancies| by the total
length of \TeX's own strings, which is currently about 23000}
@!save_size=300; {space for saving values outside of current group, must be
at most |max_halfword|}
@!trie_size=8000; {space for hyphenation patterns, should be larger for
\.{INITEX} than it is in production versions of \TeX}
@!dvi_buf_size=800; {size of the output buffer, must be a multiple of 8}
@!file_name_size=23; {file names shouldn't be longer than this}
@!pool_name='TEX.POOL[TEX,SYS] ';
{string of length |file_name_size|, where string pool appears}
@z
One can't simply make helter-skelter changes to the following constants,
since certain rather complex initialization
numbers are computed from them. They are defined here using
\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to
emphasize this distinction.
@d mem_base=0 {smallest index in the |mem| array, must not be less
than |min_halfword|}
@d hi_mem_base=25000 {smallest index in the single-word area of |mem|,
must be substantially larger than |mem_base| and smaller than |mem_max|}
@d font_base=0 {smallest internal font number, must not be less
than |min_quarterword|}
@d hash_size=2100 {maximum number of control sequences; it should be at most
about |(mem_max-hi_mem_base)/6|, but 2100 is already quite generous}
@d hash_prime=1777 {a prime number equal to about 85\% of |hash_size|}
@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
@↑system dependencies@>
@z
@ The ascii code is ``standard'' only to a certain extent, since many
computer installations have found it advantageous to have ready access
to more than 94 printing characters. Appendix@@C of the \TeX\ manual
gives a complete specification of the intended correspondence between
characters and \TeX's internal representation.
The code shown here is intended to be used on the Stanford {\sc SAIL} system,
and at other installations like CMU and ISI where essentially the same
extended character set is used. The fact that {\mc SAIL} has |'}'| in the
wrong place turns out to cause no difficulty in this case.
@<Set initial values...@>=
for i←1 to @'37 do xchr[i]←chr(i);
xchr[@'30]←chr(@'137);
xchr[@'32]←chr(@'33); {|not_equal| sign}
xchr[@'33]←chr(@'176);
@↑system dependencies@>
@z
@p function a_open_in(var f:alpha_file):boolean;
{open a text file for input}
begin reset(f,name_of_file,'/E/O/N:9');
{the \.{/E} switch distinguishes |form_feed| from |carriage_return|;
the \.{/O} switch gives error control to us;
and the \.{/N:9} switch specifies 9 buffers, which
seems to work satisfactorily at {\mc SAIL}}
a_open_in←not eof(f);
end;
@#
function a_open_out(var f:alpha_file):boolean;
{open a text file for output}
begin rewrite(f,name_of_file,'/O/N:9'); a_open_out←eof(f);
end;
@#
function b_open_in(var f:byte_file):boolean;
{open a binary file for input}
begin reset(f,name_of_file,'/B:8/O/N:9'); b_open_in←not eof(f);
end; {the \.{/B} switch is necessary to get byte packing}
@#
function b_open_out(var f:byte_file):boolean;
{open a binary file for output}
begin rewrite(f,name_of_file,'/O/N:9'); b_open_out←eof(f);
end; {here we don't pack it since |ary_out| is going to be used}
@#
function w_open_in(var f:word_file):boolean;
{open a word file for input}
begin reset(f,name_of_file,'/O/N:9'); w_open_in←not eof(f);
end;
@#
function w_open_out(var f:word_file):boolean;
{open a word file for output}
begin rewrite(f,name_of_file,'/O/N:9'); w_open_out←eof(f);
end;
@↑system dependencies@>
@z
@ Input from text files is read one line at a time, using a routine called
|input_ln|. This function is defined in terms of global variables
called |buffer|, |first|, and |last|
that will be described in detail later; for now, it suffices for us
to know that |buffer| is an array of |ascii_code| values, and that
|first| and |last| are indices into this array representing the
beginning and ending of a line of text.
We will read the lines first into an auxiliary buffer, in order to
save the running time of procedure-call overhead. This uses a nice
feature of \ph\ that Knuth chose not to mention in \TeX82.
@↑Knuth, Donald Ervin@>
At {\mc SAIL} we want to recognize page marks (indicated by |form_feed|
characters), and keep track of the current page number.
@d form_feed=@'14 {ascii code used at end of a page}
@<Glob...@>=
@!buffer:array[0..buf_size] of ascii_code; {lines of characters being read}
@!first:0..buf_size; {the first unused position in |buffer|}
@!last:0..buf_size; {end of the line just input to |buffer|}
@!max_buf_stack:0..buf_size; {largest index used in |buffer|}
@!aux_buf:array[0..70] of text_char; {where the characters go first}
@↑system dependencies@>
@z
@p function input_ln(var f:alpha_file):boolean; {inputs the next line
or returns |false|}
label 1,done;
var n: integer;
@!k,@!m: 0..buf_size; {indices into |buffer|}
begin get(f); {input the first character of the line into |f↑|}
if f↑=chr(@'12) then get(f); {skip past a |line_feed|}
if eof(f) then input_ln←false
else begin last←first;
read(f,aux_buf:n);
if buffer[first]=form_feed then {previous line was end-of-page}
begin incr(page); line←1; {adjust line and page numbers}
end;
1: if last+n>max_buf_stack then
if last+n≥buf_size then
begin max_buf_stack←buf_size;
overflow("buffer size",buf_size);
@:TeX capacity exceeded buffer size}{\quad buffer size@>
end
else max_buf_stack←last+n;
if n>0 then
begin m←last;
if n=72 then last←m+71@+else last←m+n;
for k←m to last-1 do buffer[k]←xord[aux_buf[k-m]];
if n=72 then {there's more on this line}
begin read(f,aux_buf:n); goto 1;
end;
end
else if f↑=chr(form_feed) then {end of page}
begin aux_buf[0]←f↑; n←1; goto 1;
end;
loop@+ begin if last=first then goto done;
if buffer[last-1]≠" " then goto done;
decr(last);
end;
done: input_ln←true;
end;
end;
@↑system dependencies@>
@z
@ The user's terminal acts essentially like other files of text, except
that it is used both for input and for output. When the terminal is
considered an input file, the file variable is called |term_in|, and when it
is considered an output file the file variable is |term_out|.
On WAITS, this point is moot, since we use the built-in |TTY| file.
@↑system dependencies@>
@d term_in==TTY {the terminal as an input file}
@d term_out==TTY {the terminal as an output file}
@z
@ Here is how to open the terminal files
on WAITS: we don't do anything, since |TTY| is always open.
Note that |eoln(term_in)| is initially |true|.
@↑system dependencies@>
@d t_open_in==do_nothing {open the terminal for text input}
@d t_open_out==do_nothing {open the terminal for text output}
@z
@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
@d wake_up_terminal == begin if inskp0 then end
{cancel the user's cancellation of output}
@<Error handling procedures@>=
function inskp0:boolean; extern;
@z
@ The following program does the required initialization
and accepts interrupts and also retrieves a possible command line, using
new system routines due to David R. Fuchs.
@↑Fuchs, David Raymond@>
@d pto_chr(#)==ptwr1w(0,ord(#)); {put a character in the line editor}
@p procedure esci(var x:integer); extern; @t\2@>@;
{increments |x| each time the user types escape-I or break-I;
the program can change |x| whenever it wants to, but |x| had
better be a global variable}
@#
function rescan:boolean; extern; @t\2@>@;
{puts the command line into the terminal buffer,
or returns |false| if there was no command line}
@#
function tmp_in(f:s@&t@&r@&i@&n@&g;var s:s@&t@&r@&i@&n@&g):integer; extern;
@t\2@>@;
{reads \.{TMPCOR} file |f| into |s|, and returns its length
(|≤0| means error)}
@#
function cclsw: boolean; extern; @t\2@>@;
{was program started with \.{RUN} offset of 1 (i.e., from \.{SNAIL})?}
@#
procedure ptwr1w(pty,c:integer); extern; @t\2@>@;
{simulates typing of a character on a \.{PTY}}
@#
function init_terminal:boolean; {gets the terminal files started}
label exit;
var l:integer; {length returned by |tmp_in|}
@!line_found:boolean; {have we scanned a line?}
@!tmp_cor_buf:packed array[0..100] of char; {where |tmp_in| puts things}
begin t_open_in;
esci(interrupt);
last←first;
if cclsw then {started by \.{TEX} monitor command}
begin l←tmp_in('TEX',tmp_cor_buf);
loc←1;
while (loc<l)∧(tmp_cor_buf[loc]≠'←') do incr(loc);
incr(loc);
while loc<l do
begin if tmp_cor_buf[loc]>' ' then
begin buffer[last]←xord[tmp_cor_buf[loc]]; incr(last);
end;
incr(loc);
end;
end
else
@!debug if false then@;@+gubed@;@/
if rescan then
begin read_ln(term_in); {get first character into |term_in↑|}
while (¬ eoln(term_in))∧(term_in↑≠';') do get(term_in);
if term_in↑=';' then
begin get(term_in);
while ¬ eoln(term_in) do
begin buffer[last]←xord[term_in↑]; incr(last); get(term_in);
end;
end;
end;
line_found←(last>first);
loop@+ begin loc←first;
while (loc<last)∧(buffer[loc]=" ") do incr(loc);
if loc<last then
begin init_terminal←true;
return; {return unless the line was all blank}
end;
if line_found then
write_ln(term_out,'Please type the name of your input file.');
wake_up_terminal; write(term_out,'**'); update_terminal;
@.**@>
buffer[first]←0; {|input_ln| may look at |buffer[first]|}
if not input_ln(term_in) then {this shouldn't happen}
begin write_ln(term_out);
write(term_out,'! End of file on the terminal... why?');
@.End of file on the terminal@>
init_terminal←false; return;
end;
line_found←true;
end;
exit:end;
@↑system dependencies@>
@z
@<Character |k| cannot be printed@>=
k in [0,@'11..@'15,@'33]
@↑system dependencies@>
@z
@p procedure term_input; {gets a line from the terminal}
var k:0..buf_size; {index into |buffer|}
begin update_terminal; {Now the user sees the prompt for sure}
buffer[first]←0; {makes sure |input_ln| doesn't find a |form_feed|}
if not input_ln(term_in) then fatal_error("End of file on the terminal!");
@.End of file on the terminal@>
term_offset←0; {the user's line ended with carriage return}
decr(selector); {prepare to echo the input}
if last≠first then for k←first to last-1 do print(buffer[k]);
print_ln; incr(selector); {restore previous status}
end;
@↑system dependencies@>
@z
@ It is desirable to provide an `\.E' option here that gives the user
an easy way to return from \TeX\ to the system editor, with the offending
line ready to be edited. The present implementation does this by loading
the line editor with the appropriate call to the editor. We treat `\.T' the
same as `\.E', because other programs on this system invoke the editor
when the user says `\.T'.
There is a secret `\.D' option available when the debugging routines have
not been commented out.
@↑debugging@>
@<Interpret code |c| and |return| if done@>=
case c of
"1","2","3","4","5","6","7","8","9": if deletions_allowed then
@<Delete |c-"0"| tokens, |goto continue|@>;
@t\4\4@>@;@+@!debug "D": begin debug_help; goto continue;@+end;@+gubed@/
"E","T": if base_ptr>0 then
begin selector←new_string; pool_ptr←str_start[str_ptr];
print("et "); print(input_stack[base_ptr].name_field);
print_char("/"); print_int(page); print("p/");
print_int(line); print_char("l");
if str_ptr<max_strings then
begin pseudo_typein←str_ptr; incr(str_ptr);
str_start[str_ptr]←pool_ptr;
end; {|make_string| not declared |forward|}
selector←term_and_log; interaction←scroll_mode; jump_out;
end;
"H": @<Print the help information, |goto continue|@>;
"I":@<Introduce new material from the terminal and |return|@>;
"Q","R","S":@<Change the interaction level and |return|@>;
"X":begin interaction←scroll_mode; jump_out;
end;
othercases do_nothing
endcases;@/
@<Print the menu of available options@>
@↑system dependencies@>
@z
It is usually most efficient to have |min_quarterword=min_halfword=0|,
so one should try to achieve this unless it causes a severe problem.
The values defined here are recommended for most 36-bit computers.
@d min_quarterword=0 {smallest allowable value in a |quarterword|}
@d max_quarterword=511 {largest allowable value in a |quarterword|}
@d min_halfword==0 {smallest allowable value in a |halfword|}
@d max_halfword==262143 {largest allowable value in a |halfword|}
@↑system dependencies@>
@z
The inner loop of \TeX\ will run faster with respect to compilers
that don't optimize expressions like `|x+0|' and `|x-0|', if these
macros are simplified in the obvious way when |min_quarterword=0|.
So they have been simplified here in the obvious way.
@↑inner loop@>
@d qi(#)==# {to put an |eight_bits| item into a quarterword}
@d qo(#)==# {to take an |eight_bits| item from a quarterword}
@d hi(#)==# {to put a sixteen-bit item into a halfword}
@d ho(#)==# {to take a sixteen-bit item from a halfword}
@z
cat_code(carriage_return)←car_ret; cat_code(" ")←spacer;
cat_code("\")←escape; cat_code(form_feed)←car_ret;
cat_code(invalid_code)←invalid_char; cat_code(null_code)←ignore;
for k←"0" to "9" do math_code(k)←hi(k+var_code);
for k←"A" to "Z" do
begin cat_code(k)←letter; cat_code(k+"a"-"A")←letter;@/
math_code(k)←hi(k+var_code+@"100);
math_code(k+"a"-"A")←hi(k+"a"-"A"+var_code+@"100);@/
lc_code(k)←k+"a"-"A"; lc_code(k+"a"-"A")←k+"a"-"A";@/
uc_code(k)←k; uc_code(k+"a"-"A")←k;@/
sf_code(k)←999;
end;
@z
@ The following procedure, which is called just before \TeX\ initializes its
input and output, establishes the initial values of the date and time.
It uses a {\mc WAITS} monitor call that puts the date in the left 18 bits
and the time in the right 18 bits.
@p procedure fix_date_and_time;
var t:integer; {accumulator}
date:integer; {raw date}
g:boolean; {garbage}
begin calli(@'400101,,t,t,g); {that's \.{ACCTIM}}
date←t div @'1000000;
time←(t mod @'1000000) div 60;
day←(date mod 31)+1;
month←((date div 31) mod 12)+1;
year←(date div (31*12))+1964;
end;
@↑system dependencies@>
@z
The global variable |line| contains the line number in the topmost
open file, for use in error messages. If we are not reading from
the terminal, |line_stack[index]| holds the line number for the
enclosing level, so that |line| can be restored when the current
file has been read.
Similarly, we maintain a global variable |page| and a corresponding
|page_stack|.
@d terminal_input==(name=0) {are we reading from the terminal?}
@d cur_file==input_file[index] {the current |alpha_file| variable}
@<Glob...@>=
@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
@!input_file : array[1..max_in_open] of alpha_file;
@!line : integer; {current line number in the current source file}
@!line_stack : array[0..max_in_open] of integer;
@!page : integer; {current page number in the current source file}
@!page_stack : array[0..max_in_open] of integer;
@↑system dependencies@>
@z
@<Print location of current line@>=
if name≤16 then
if terminal_input then
if base_ptr=0 then print_nl("<*>") else print_nl("<insert> ")
else begin print_nl("<read "); print_int(name-1); print_char(">");
end
else begin if page>1 then
begin print_nl("p."); print_int(page); print(",l.");
end
else print_nl("l.");
print_int(line);
end;
print_char(" ")
@↑system dependencies@>
@z
@ The |begin_file_reading| procedure starts a new level of input for lines
of characters to be read from a file, or as an insertion from the
terminal. It does not take care of opening the file, nor does it set |loc|
or |limit| or |line| or |page|.
@p procedure begin_file_reading;
begin if in_open=max_in_open then overflow("text input levels",max_in_open);
@:TeX capacity exceeded text input levels}{\quad text input levels@>
if first=buf_size then overflow("buffer size",buf_size);
@:TeX capacity exceeded buffer size}{\quad buffer size@>
incr(in_open); push_input; index←in_open;
line_stack[index]←line; start←first; state←mid_line;
name←0; {|terminal_input| is now |true|}
page_stack[index]←page;
end;
@↑system dependencies@>
@z
@ Conversely, the variables must be downdated when such a level of input
is finished:
@p procedure end_file_reading;
begin first←start; page←page_stack[index]; line←line_stack[index];
if name>16 then a_close(cur_file); {forget it}
pop_input; decr(in_open);
end;
@↑system dependencies@>
@z
@p procedure check_outer_validity;
var p:pointer; {points to inserted token list}
@!q:pointer; {auxiliary pointer}
begin if scanner_status≠normal then
begin @<Back up an outer control sequence so that it can be reread@>;
if scanner_status>skipping then
@<Tell the user what has run away and try to recover@>
else begin print_err("Incomplete "); print_cmd_chr(if_test,cur_if);
@.Incomplete \\if...@>
print("; all text was ignored after line "); print_int(skip_line);
if skip_page>1 then
begin print(", p."); print_int(skip_page);
end;
help3("A forbidden control sequence occurred in skipped text.")@/
("This kind of error happens when you say `\if...' and forget")@/
("the matching `\fi'. I've inserted a `\fi'; this might work.");
if cs_ptr≠0 then cs_ptr←0
else help_line[2]←@|
"The file ended while I was skipping conditional text.";
cur_tok←cs_token_flag+frozen_fi; ins_error;
end;
end;
end;
@↑system dependencies@>
@z
@ If the user has set the |pausing| parameter to some nonzero value,
and if nonstop mode has not been selected,
each line of input is displayed in the transcript file, followed by `\.{=>}',
and also put into the user's line-editor buffer.
\TeX\ waits for the line to be edited, and the next line received is
used instead of the line in the file.
@p procedure firm_up_the_line;
var k:0..buf_size; {an index into |buffer|}
begin limit←last;
if (pausing≠0)∧(interaction>nonstop_mode)∧(buffer[start]≠form_feed) then
begin print_ln;
if start=limit then {empty line will be made nonempty so that it's visible}
begin buffer[start]←" "; incr(limit);
end;
decr(selector); {inhibit terminal output temporarily}
for k←start to limit-1 do
begin print_char(buffer[k]);
pto_chr(xchr[buffer[k]]);
end;
print("=>"); first←start;
if not input_ln(term_in) then fatal_error("End of file on the terminal!");
@.End of file on the terminal@>
if last>first then for k←first to last-1 do print_char(buffer[k]);
limit←last; print_ln; incr(selector);
end;
end;
@↑system dependencies@>
@z
@ The first line of a file must be treated specially, since |input_ln|
starts with |get|. Furthermore we want to omit the optional file
directory page present at {\mc SAIL}.
@↑system dependencies@>
@<Input the first line of |read_file[n]|@>=
begin if eoln(read_file[n]) then last←start
else begin buffer[start]←xord[read_file[n]↑]; first←start+1;
if ¬ input_ln(read_file[n]) then confusion("read");
@:this can't happen read}{\quad read@>
if(last-start=29)∧(buffer[start]="C")∧(buffer[start+8]=@'26) then
begin while (read_file[n]↑≠chr(form_feed))∧(not eof(read_file[n])) do
begin read_ln(read_file[n]); read(read_file[n],aux_buf:temp_ptr);
end; {skip the directory}
buffer[start]←form_feed; last←start+1;
end;
end;
read_open[n]←normal;
end
@↑system dependencies@>
@z
@ When we skip conditional text, we keep track of the line number
where skipping began, for use in error messages. Also the page number.
@<Glob...@>=
@!skip_line,@!skip_page:integer; {skipping began here}
@↑system dependencies@>
@z
@p procedure pass_text;
label done;
var l:integer;
@!save_scanner_status:small_number; {|scanner_status| upon entry}
begin save_scanner_status←scanner_status; scanner_status←skipping; l←0;
skip_line←line; skip_page←page;
loop@+ begin get_next;
if cur_cmd=fi_or_else th¬n
begin if l=0 then goto done;
if cur_chr=fi_code then l←l-1;
end
else if cur_cmd=if_test then l←l+1;
end;
done: scanner_status←save_scanner_status;
end;
@↑system dependencies@>
@z
@ The file names we shall deal with have the following structure:
If the name contains `\.[', the file area consists of all characters
from this character to the end; otherwise the file area is null.
If the remaining file name contains `\..', the file extension consists of all
such characters from this character to the end, otherwise the file extension
is null. We can assume that there is at most one `\.[' and at most one `\..'.
We can scan such file names easily by using two global variables that keep track
of the occurrences of area and extension delimiters:
@<Glob...@>=
@!area_delimiter:pool_pointer; {the most recent `\.[', if any}
@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
@↑system dependencies@>
@z
@ Input files that can't be found in the user's area may appear in a standard
system area called |TEX_area|. Font files whose areas are not given explicitly
are assumed to appear in a standard system area called |TEX_font_area|.
These system area names will, of course, vary from place to place.
@d TEX_area=="[tex,sys]"
@d TEX_font_area=="[tex,sys]"
@↑system dependencies@>
@z
@ And here's the second.
@p function more_name(c:ascii_code):boolean;
begin if c=" " then more_name←false
else begin if c="[" then area_delimiter←pool_ptr
else if c="." then ext_delimiter←pool_ptr;
str_room(1); append_char(c); {contribute |c| to the current string}
more_name←true;
end;
end;
@↑system dependencies@>
@z
@ The third.
@p procedure end_name;
begin if str_ptr+3>max_strings then overflow("number of strings",max_strings);
@:TeX capacity exceeded number of strings}{\quad number of strings@>
cur_name←str_ptr;
if ext_delimiter=0 then cur_ext←""
else begin incr(str_ptr);
str_start[str_ptr]←ext_delimiter; cur_ext←str_ptr;
end;
if area_delimiter≤str_start[str_ptr] then
begin cur_area←""; incr(str_ptr); str_start[str_ptr]←pool_ptr;
end
else begin incr(str_ptr);
str_start[str_ptr]←area_delimiter; cur_area←make_string;
end;
end;
@↑system dependencies@>
@z
@ Conversely, here is a routine that takes three strings and prints a file
name that might have produced them. (The routine is system dependent, because
some operating systems put the file area last instead of first.)
@<Basic printing...@>=
procedure print_file_name(@!n,@!a,@!e:str_number);
begin print(n); print(e); print(a);
end;
@↑system dependencies@>
@z
@ Another system-dependent routine is needed to convert three \TeX\ strings
into the |name_of_file| value that is used to open files.
A special convention is used here with respect to font metric files: If the
file name is longer than six characters (e.g., `\.{helvetica}' or
`\.{oldenglish}'), we abbreviate it by retaining the first three and
last three characters (e.g., `\.{helica}' or `\.{oldish}').
@d append_to_name(#)==begin c←#; incr(k);
if (c≥"a")∧(c≤"z") then c←c-@'40; {convert to upper case}
if k≤file_name_size then name_of_file[k]←xchr[c];
end
@p procedure pack_file_name(@!n,@!a,@!e:str_number);
var k:integer; {number of positions filled in |name_of_file|}
@!c: ascii_code; {character being packed}
@!j:pool_pointer; {index into |string_pool|}
begin k←0;
if (e=".tfm")∧(length(n)>6) then
begin for j←str_start[n] to str_start[n]+2 do
append_to_name(str_pool[j]);
for j←str_start[n+1]-3 to str_start[n+1]-1 do
append_to_name(str_pool[j]);
end
else for j←str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
for j←str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
for j←str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]);
if k≤file_name_size then name_length←k@+else name_length←file_name_size;
for k←name_length+1 to file_name_size do name_of_file[k]←' ';
end;
@↑system dependencies@>
@z
@ A messier routine is also needed, since format file names must be scanned
before \TeX's string mechanism has been initialized. We shall use the
global variable |TEX_format_default| to supply the text for default system
areas and extensions related to format files.
@d format_default_length=18 {length of the |TEX_format_default| string}
@d format_area_length=9 {length of its area part}
@d format_ext_length=4 {length of its `\.{.fmt}' part}
@<Glob...@>=
@!TEX_format_default:packed array[1..format_default_length] of char;
@↑system dependencies@>
@z
@ @<Set init...@>=
TEX_format_default←'PLAIN.fmt[tex,sys]';
@.PLAIN@>
@↑system dependencies@>
@z
@ Here is the messy routine that was just mentioned. It sets |name_of_file|
from the first |n| characters of |TEX_format_default|, followed by
|buffer[a..b]|, followed by the last |format_ext_length| characters of
|TEX_format_default|; but it actually switches stuff around to keep the
file area last.
We dare not give error messages here, since \TeX\ calls this routine before
the |error| routine is ready to roll. Instead, we simply drop excess characters,
since the error will be detected in another way when a strange file name
isn't found.
@p procedure pack_buffered_name(@!n:small_number;@!a,@!b:integer);
var k:integer; {number of positions filled in |name_of_file|}
@!c: ascii_code; {character being packed}
@!j:integer; {index into |buffer| or |TEX_format_default|}
@!d:integer; {a kludge}
begin if n+b-a+1+format_ext_length>file_name_size then
b←a+file_name_size-n-1-format_ext_length;
k←0;
for j←a to b do append_to_name(buffer[j]);
if b=0 then
begin d←format_default_length-format_area_length+1;
n←format_default_length;
end
else d←format_default_length-format_area_length-format_ext_length+1;
for j←d to format_default_length-format_area_length do
append_to_name(xord[TEX_format_default[j]]);
for j←format_default_length-n+1 to format_default_length do
append_to_name(xord[TEX_format_default[j]]);
if k≤file_name_size then name_length←k@+else name_length←file_name_size;
for k←name_length+1 to file_name_size do name_of_file[k]←' ';
end;
@↑system dependencies@>
@z
@ Operating systems often make it possible to determine the exact name (and
possible version number) of a file that has been opened. The following routine,
which simply makes a \TeX\ string from the value of |name_of_file|, should
ideally be changed to deduce the full name of file@@|f|,
if it is possible to do this in a \PASCAL\ program.
Here is how Dave Fuchs taught \TeX\ to do this on {\mc WAITS}:
@↑Fuchs, David Raymond@>
@p procedure cur_nam(var chan:f@&i@&l@&e;var s:string); extern; @t\2@>@/
function make_name_string(var f:f@&i@&l@&e):str_number;
var s:packed array[1..24] of char;
@!k:1..24; {file names at {\sc SAIL} have at most 23 characters}
begin cur_nam(f,s); str_room(24); k←1;
while ord(s[k])≠0 do
begin append_char(xord[s[k]]); incr(k);
end;
make_name_string←make_string;
end;
function a_make_name_string(var f:alpha_file):str_number;
begin a_make_name_string←make_name_string(f);
end;
function b_make_name_string(var f:byte_file):str_number;
begin b_make_name_string←make_name_string(f);
end;
function w_make_name_string(var f:word_file):str_number;
begin w_make_name_string←make_name_string(f);
end;
@↑system dependencies@>
@z
@p procedure prompt_file_name(@!s,@!e:str_number);
label done;
var k:0..buf_size; {index into |buffer|}
@!i:pool_pointer; {index into |str_pool|}
begin if interaction=scroll_mode then wake_up_terminal;
if s="input file name" then print_err("I can't find file `")
@.I can't find file x@>
else print_err("I can't write on file `");
@.I can't write on file x@>
print_file_name(cur_name,cur_area,cur_ext); print("'.");
if e=".tex" then show_context;
print_nl("Please type another "); print(s);
@.Please type...@>
if interaction<scroll_mode then
fatal_error("*** (job aborted, file error in nonstop mode)");
@.job aborted, file error...@>
clear_terminal; {now we'll fill the line editor's buffer with the old name}
for i←str_start[cur_name] to str_start[cur_name+1]-1 do
pto_chr(xchr[str_pool[i]]);
for i←str_start[cur_ext] to str_start[cur_ext+1]-1 do
pto_chr(xchr[str_pool[i]]);
for i←str_start[cur_area] to str_start[cur_area+1]-1 do
pto_chr(xchr[str_pool[i]]);
ptwr1w(0,@'214); {control-formfeed returns cursor to start of line}
prompt_input(": "); @<Scan file name in the buffer@>;
if cur_ext="" then cur_ext←e;
pack_cur_name;
end;
@z
@ Here we have to remember that the |input_ln| routine
starts with a |get|. Also, we want to skip past the editor's directory
page if it is present.
@<Read the first line...@>=
begin if eoln(cur_file) then last←start
else begin buffer[start]←xord[cur_file↑]; first←start+1;
if ¬ input_ln(cur_file) then confusion("input");
@:this can't happen input}{\quad input@>
if(last-start=29)∧(buffer[start]="C")∧(buffer[start+8]=@'26) then
begin while (cur_file↑≠chr(form_feed))∧(not eof(cur_file)) do
begin read_ln(cur_file); read(cur_file,aux_buf:temp_ptr);
end; {skip the directory}
buffer[start]←form_feed; last←start+1;
end;
end;
firm_up_the_line;
buffer[limit]←carriage_return; first←limit+1; loc←start; line←1; page←1;
end
@↑system dependencies@>
@z
@!dvi_index=0..dvi_buf_size; {an index into the output buffer}
@!packed_bytes=packed array[dvi_index] of eight_bits;
{buffer for \.{DVI} output}
@↑system dependencies@>
@z
@ Some systems may find it more efficient to make |dvi_buf| a |packed|
array, since output of four bytes at once may be facilitated.
@<Glob...@>=
@!dvi_buf:packed_bytes; {buffer for \.{DVI} output}
@!half_buf:dvi_index; {half of |dvi_buf_size|}
@!dvi_limit:dvi_index; {end of the current half buffer}
@!dvi_ptr:dvi_index; {the next available buffer address}
@!dvi_offset:integer; {|dvi_buf_size| times the number of times the
output buffer has been emptied}
@!dvi_gone:integer; {the number of bytes already output to |dvi_file|}
@↑system dependencies@>
@z
@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
|write_dvi(a,b)|. For best results, this procedure should be optimized to
run as fast as possible on each particular system, since it is part of
\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be
multiples of 4 when |write_dvi(a,b)| is called; therefore it is possible on
many machines to use efficient methods to pack four bytes per word and to
output an array of words with one system call.
@↑inner loop@>
@p procedure ary_out(var f:file;@!b:packed_bytes; @!o,@!c:integer);
extern;@t\2@>@#
procedure write_dvi(@!a,@!b:dvi_index);
begin ary_out(dvi_file,dvi_buf,a div 4,(b+1-a)div 4);
end;
@↑system dependencies@>
@z
@<Finish the \.{DVI} file@>=
if total_pages=0 then print_nl("No output file.")
@.No output file@>
else begin dvi_out(post); {beginning of the postamble}
dvi_four(last_bop); last_bop←dvi_offset+dvi_ptr-5; {|post| location}
dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
prepare_mag; dvi_four(mag); {magnification factor}
dvi_four(max_v); dvi_four(max_h);@/
dvi_out(max_push div 256); dvi_out(max_push mod 256);@/
dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
@<Output the font definitions for all fonts that were used@>;
dvi_out(post_post); dvi_four(last_bop); dvi_out(id_byte);
k←4+((dvi_buf_size-dvi_ptr) mod 4); {the number of 223's}
while k>0 do
begin dvi_out(223); decr(k);
end;
@<Empty the last bytes out of |dvi_buf|@>;
print_nl("Output written on "); print(output_file_name);
@.Output written on x@>
print(" ("); print_int(total_pages); print(" page");
if total_pages≠1 then print_char("s");
print(", "); print_int(dvi_offset+dvi_ptr); print(" bytes).");
b_close(dvi_file);
if pseudo_typein=0 then
begin k←selector; selector←new_string;
pool_ptr←str_start[str_ptr];
print("r DVIdover;"); print(output_file_name);
selector←k; str_room(1);
pseudo_typein←make_string;
end;
end
@↑system dependencies@>
@z
@ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
if output_active then print(") has occurred while \output is active")
else begin if pack_begin_line≠0 then
begin if pack_begin_line>0 then print(") in paragraph at lines ")
else print(") in alignment at lines ");
print_int(abs(pack_begin_line));
print("--");
end
else print(") detected at line ");
print_int(line);
if page>1 then
begin print(", p."); print_int(page);
end;
end;
print_ln;@/
font_in_short_display←null_font; short_display(list_ptr(r)); print_ln;@/
begin_diagnostic; show_box(r); end_diagnostic(true)
@↑system dependencies@>
@z
@ @<Finish issuing a diagnostic message for an overfull or underfull vbox@>=
if output_active then print(") has occurred while \output is active")
else begin if pack_begin_line≠0 then {it's actually negative}
begin print(") in alignment at lines ");
print_int(abs(pack_begin_line));
print("--");
end
else print(") detected at line ");
print_int(line);
if page>1 then
begin print(", p."); print_int(page);
end;
print_ln;@/
end;
begin_diagnostic; show_box(r); end_diagnostic(true)
@↑system dependencies@>
@z
@ Here we do whatever is needed to complete \TeX's job gracefully
on the local operating system.
The new stuff at {\mc SAIL} has to do with preparing for what the user
presumably wants to do next, by typing it for him/her.
@<Last-minute...@>=
procedure close_files_and_terminate;
var k:integer; {all-purpose index}
begin @<Finish the extensions@>;
@!stat if tracing_stats≠0 then @<Output statistics about this job@>;@;@+tats@/
@<Finish the \.{DVI} file@>;
if job_name>0 then
begin write_ln(log_file); a_close(log_file);
end;
if (pseudo_typein≠0)∧(interaction>batch_mode) then
begin write_ln(term_out);
for k←str_start[pseudo_typein] to str_start[pseudo_typein+1]-1 do
pto_chr(xchr[str_pool[k]]);
end;
end;
@↑system dependencies@>
@z
@* \[54] System-dependent changes.
Here are the remaining things needed to make the implementation
complete at {\mc SAIL}.
@↑system dependencies@>
@ The |pseudo_typein| variable is set nonzero if the |error| routine
uses the `\.E' option to exit and edit.
@<Glob...@>=
@!pseudo_typein:str_number;
@ @<Set init...@>=
pseudo_typein←0; page←0;
@z